package com.bytz.modules.cms.order.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bytz.common.exception.BytzBootException;
import com.bytz.modules.cms.domain.PayProcessor;
import com.bytz.modules.cms.domain.impl.pay.BalancePayProcessor;
import com.bytz.modules.cms.domain.impl.pay.PrepayProcessor;
import com.bytz.modules.cms.domain.model.pay.BalancePayIn;
import com.bytz.modules.cms.domain.model.pay.PayOut;
import com.bytz.modules.cms.domain.model.pay.PrepayIn;
import com.bytz.modules.cms.domain.model.pay.PrepayPrepareContext;
import com.bytz.modules.cms.order.constant.OrderPayConstants;
import com.bytz.modules.cms.order.entity.Order;
import com.bytz.modules.cms.order.entity.OrderItem;
import com.bytz.modules.cms.order.entity.OrderPay;
import com.bytz.modules.cms.order.entity.OrderPayDetail;
import com.bytz.modules.cms.order.mapper.OrderPayMapper;
import com.bytz.modules.cms.order.model.PayNotifyResult;
import com.bytz.modules.cms.order.service.IOrderOfflinePayService;
import com.bytz.modules.cms.order.service.IOrderPayDetailService;
import com.bytz.modules.cms.order.service.IOrderPayService;
import com.bytz.modules.cms.order.service.IOrderPickUpDetailService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * <p>
 * 莱宝CMS—订单管理-订单交易记录表 服务实现类
 * </p>
 *
 * @author KyleWang
 * @since 2022-10-10
 */
@Service
public class OrderPayServiceImpl extends ServiceImpl<OrderPayMapper, OrderPay> implements IOrderPayService {

    @Autowired
    private IOrderPayDetailService orderPayDetailService;

    @Autowired
    @Lazy
    private PrepayProcessor prepayProcessor;

    @Autowired
    @Lazy
    private BalancePayProcessor balancePayProcessor;

    @Autowired
    private IOrderPickUpDetailService pickUpDetailService;

    @Autowired
    protected IOrderOfflinePayService orderOfflinePayService;


    @Override
    public List<OrderPay> getByOrderId(String orderId) {

        if (StringUtils.isBlank(orderId)) {
            return new ArrayList<>();
        }

        return lambdaQuery().eq(OrderPay::getOrderId, orderId).list();
    }

    @Transactional(rollbackFor = Exception.class)
    @Lock4j(keys = {"T(com.bytz.modules.cms.reseller.util.ResellerSecurityUtils).getUserId()"})
    @Override
    public PayOut prepay(PrepayIn payIn) {
        @SuppressWarnings("unchecked")
        PayProcessor<PrepayIn> payProcessor = (PayProcessor<PrepayIn>)getPayProcessor(OrderPayConstants.PayType.PREPAY);

        return payProcessor.prepare(payIn);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @Lock4j(keys = {"#payNotifyResult.payDetailId"})
    public void notify(PayNotifyResult payNotifyResult) {
        String payDetailId = payNotifyResult.getPayDetailId();
        OrderPayDetail orderPayDetail = orderPayDetailService.getById(payDetailId);
        if (orderPayDetail == null) {
            throw new BytzBootException("找不到支付明细");
        }

        String orderPayId = orderPayDetail.getOrderPayId();
        OrderPay orderPay = getById(orderPayId);
        if (orderPay == null) {
            throw new BytzBootException("找不到支付主记录");
        }

        if (!OrderPayConstants.PayStatus.PAYING.equals(orderPay.getStatus())) {
            // 如果不是支付中，则说明已经处理过，不再继续处理
            return;
        }

        // 获取处理器
        PayProcessor<?> payProcessor = getPayProcessor(orderPay.getPayType());

        // 执行操作
        payProcessor.notify(payNotifyResult, orderPay, orderPayDetail);
    }

    /**
     * 获取支付处理器
     */
    private PayProcessor<?> getPayProcessor(String payType) {
        if (OrderPayConstants.PayType.PREPAY.equals(payType)) {
            return prepayProcessor;
        } else if (OrderPayConstants.PayType.BALANCE.equals(payType)) {
            return balancePayProcessor;
        }

        throw new BytzBootException("未找到处理器");
    }

    @Override
    public boolean hasPayingRecord(String orderId) {
        return lambdaQuery().eq(OrderPay::getOrderId, orderId)
                .eq(OrderPay::getStatus, OrderPayConstants.PayStatus.PAYING)
                .count() > 0;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void submitPay(String orderPayId) {
        updateStatus(orderPayId, OrderPayConstants.PayStatus.DONE);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void rollbackPay(String orderPayId) {
        updateStatus(orderPayId, OrderPayConstants.PayStatus.FAILED);
    }

    @Override
    public PayOut balancePay(BalancePayIn balance) {
        @SuppressWarnings("unchecked")
        PayProcessor<BalancePayIn> payProcessor = (PayProcessor<BalancePayIn>)getPayProcessor(OrderPayConstants.PayType.BALANCE);

        return payProcessor.prepare(balance);
    }

    @Override
    public BigDecimal calculatePrepayPrice(Order order, List<OrderItem> orderItems) {
        PrepayIn prepayIn = new PrepayIn();
        prepayIn.setOrderId(order.getId());

        PrepayPrepareContext context = new PrepayPrepareContext();
        context.setOrder(order);
        context.setOrderItems(orderItems);

        prepayProcessor.calculatePayAmount(prepayIn, context);

        return context.getPayAmount();
    }

    /**
     * 将提货明细、pay主数据、pay明细数据状态更新成交易失败
     * 将线下支付记录更新失败，并释放汇票
     * 此方法可以直接使用回调的方法
     * @param orderPayDetail 订单交易支付明细
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rollbackPay(OrderPayDetail orderPayDetail){
        OrderPay orderPay = getById(orderPayDetail.getOrderPayId());
        // 如果在线支付的金额和支付金额不一样说明有线下支付
        if (!Objects.equals(orderPay.getTotalPayAmount(), orderPayDetail.getPayAmount())) {
            List<OrderPayDetail> offlinePayDetails = orderPayDetailService.getOfflinePayByPayId(orderPay.getId());

            // 处理汇票
            orderOfflinePayService.releaseOfflinePay(offlinePayDetails);
            // 处理支付明细
            orderPayDetailService.rollbackOfflinePay(orderPay.getId());
        }
        // 提货明细表该记录置为失败
        pickUpDetailService.rollbackBalancePay(orderPayDetail.getOrderPayId());
        // pay主数据状态更新
        rollbackPay(orderPayDetail.getOrderPayId());
        // pay明细数据状态更新
        orderPayDetailService.lambdaUpdate()
                .set(OrderPayDetail::getErrorMessage, "支付订单超时【当前日期（" + DateUtil.now() + "）已经超过过期时间（" + orderPayDetail.getExpirationTime() + "）】")
                .set(OrderPayDetail::getStatus, OrderPayConstants.PayStatus.FAILED)
                .eq(OrderPayDetail::getId, orderPayDetail.getId())
                .update();
    }

    /**
     * 更新状态
     */
    private void updateStatus(String orderPayId, String status) {
        lambdaUpdate()
                .set(OrderPay::getStatus, status)
                .set(OrderPay::getCompleteDateTime, LocalDateTime.now())
                .eq(OrderPay::getId, orderPayId)
                .update();
    }
}
